/**
 * @brief   RUDP协议基础实现模块。
 * @version 1.0
 * @date    2018-3-2
 * @author  宋炜
*/

#ifndef __RDUP_HPP__
#define __RDUP_HPP__
#include <string>
#include <atomic>
#include <map>
#include <uv.h>
#include "timer.hpp"

class rudp
{
	friend void on_rudp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags);
	friend void on_rudp_send(uv_udp_send_t* req, int status);
	friend void on_rudp_getaddrinfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res);
public:
	/**
	 * 
	 */
	enum err_code{
		ERR_BASE = -1000,
		ERR_ALLOC_MEM ,
		ERR_DATA_NULL,
		ERR_OBJ_NULL,
		ERR_INIT_UDP,
		ERR_INIT_UV,
		ERR_INIT_UV_LOOP,
		ERR_UV_UDP_SEND,
		ERR_SERVER_IS_DOWN,                  // 服务器没有开启，客户端连接失败
		ERR_UV_DNS_RESOLVE,
		OK = 0
	};
	/**
	 * 
	 */
	typedef struct clientItem{
		int     id;                           // ID
		struct  sockaddr * peer;              // 对点
		clientItem( int id , struct sockaddr* addr );
		~clientItem( );
	}rudpclient_t;
  
       typedef std::map< int , rudpclient_t *>  clientMap_t;
       

        /* 协议数据包 */
	struct stqos_ip{ 
		int           qos:4;               // QOS
		int           :4;                  // 空
		int           ip_type:8;           // IP 类型
	};
	typedef struct stprotocol{
		uint16_t            msg_id;            // 消息序号
		uint16_t            start_id;
		stqos_ip            qos_ip;
		uint16_t            opt;               // 数据包类型
		uint16_t            len;               // 数据长度
		uint16_t            pkg_count;         // 数据包数量
		unsigned char     * data;              // 数据
		stprotocol        * next;
		int                 escape_cur;        // 退避选择指示，如果是指数退避，则从指数退避数组选择；如果是
		                                       // 斐波那契退避，则从斐波那契退避数组选择
		CTimer              retreat_timer;     // 退避时钟
		stprotocol();
		stprotocol( uint16_t id , uint16_t opt , uint8_t qos );
		stprotocol( uint16_t id , uint16_t opt , uint8_t qos , int len , unsigned char * data );
		~stprotocol();
		/**
		 * @brief 获取平坦数据，返回的数据是可以提交UDP进行发送操作的数据包
		 * @param data , 平台化的数据包
		 * @return 成功操作返回数据包长度，否则返回错误代码
		 */
  	        int operator()( char ** data );
	}rudp_pkge_t;
	
	/* 协议缓冲区 ， 如果接受到的数据是多包的数据则将数据暂时存在这个缓冲区内 */
	typedef struct stprotocol_buff{
		size_t 	count;						            // 总包数量
		std::map< uint16_t , rudp_pkge_t*> data;        // 数据

		stprotocol_buff();
		virtual ~stprotocol_buff();
		/**
		 * @brief 获取原始数据。
		 * @param data
		 * @return 成功返回数据长度否则返回错误代码
		*/
		int get( char ** data );
		/**
		 * @brief 插入数据
		*/
		void insert( rudp_pkge_t * pkg );
		/**
		 * @brief 清空缓冲
		*/
		void clear();

		size_t size();
	}rudp_buf_t;
	
	const static uint8_t QOS0         = 0x0;            // 不重发，不接收响应
	const static uint8_t QOS1         = 0x01;           // 不重发，接收响应
	const static uint8_t QOS2         = 0x02;           // 连续重发 ， 接收响应;在没有接收到响应前按照50ms间隔连续重发消息
	const static uint8_t QOS3         = 0x03;           // 指数退避
	const static uint8_t QOS4         = 0x04;           // 斐波那契退避。退避操作以100ms为基本的退避单位
	const static uint8_t QOS5         = 0X05;           // 连续重发5次，不接收响应 , 以20MS为间隔
	/* 操作类型定义 */
	const static uint16_t SEND        = 0X1000;         // 发送操作数据包
	const static uint16_t SEND_E      = 0X0010;         // 发送反馈包
	const static uint16_t KEEPALIVE   = 0X2000;
	const static uint16_t KEEPALIVE_E = 0X0020;
	const static uint16_t PING        = 0X4000;
	const static uint16_t PING_E      = 0X0040;
	const static uint16_t CONNECT     = 0X8000;
	const static uint16_t CONNECT_E   = 0X0080;
	const static uint16_t CLOSE       = 0XA000;
	const static uint16_t CLOSE_E     = 0X00A0;
	
	const static long      DEFAULT_ABORT_TICK  = 1000;  // 放弃接收响应时间
	/* 指数退避时钟索引表 */
        const static int EXP_ARRAY[ 10 ];
	/* 斐波那契数列退避索引表 */
	const static int FIB_ARRAY[ 10 ];

	typedef struct sendreq{
		/*  发送数据请求句柄结构:
		 * uv_udp_send_t * ---> data ---->    obj ， 提交发送的来源对象
		 *                                     |
		 *                                     +------->   buf ------> base , 要发送的数据
		 *                                                  |
		 *                                                  +---------> len  要发送的数据的长度
		*/
		rudp       * obj;      // 操作对象
		uv_buf_t   * buf;      // 数据缓冲区

		sendreq(){ obj = nullptr; buf = nullptr;}
		/**
		 * @brief 构造发送请求
		 * @param s
		 * @param data
		 * @param obj
		*/
		sendreq( size_t s , const char * data , rudp * obj );
		~sendreq();
	}sendReq_t;

	const static uint16_t STATUS_READY     = 0;
	const static uint16_t STATUS_CONNECT_1 = 1;
	const static uint16_t STATUS_CONNECT_2 = 2;
	const static uint16_t STATUS_CONNECTED = 3;
	const static uint16_t STATUS_SENDING   = 4;
	const static uint16_t STATUS_SEND      = 5;
	const static uint16_t STATUS_WAIT_ECHO = 6;
	const static uint16_t STATUS_ECHOED    = 7;
	const static uint16_t STATUS_RECVED    = 8;
	const static uint16_t STATUS_ON_ECHOED = 9;
	const static uint16_t STATUS_CLOSING   = 10;
	const static uint16_t STATUS_CLOSED    = 11;
	const static uint16_t STATUS_BINDED    = 12;
	const static uint16_t STATUS_OVERTIME  = 13;
protected:
	std::string             url;
	int                     port;
        static size_t           sock_slice;                    // 数据分片长度
	bool                    is_keepalive;                  // 是否开启包活数据
	bool                    is_keepalive_e;                // 是否开启包活响应
	long                    keepalive_intval;              // 包活间隔
	CTimer                  keep_alive_timer;              // 包活计时器
	bool                    ping_e;                        // 是否开ping响应
	int                     ping_count;                    // ping 发送的目标总数
	int                     ping_intval;                   // ping发送间隔
	struct sockaddr         peer;                          // 远程端点
	CTimer                  ping_timer;                    // ping 操作计时器
	
	err_code                m_error;
	
	uint32_t                send_count;                    // 当前数据重发计数

	static uint16_t         m_send_id;                     // 主动发送ID编号
	std::map< uint32_t , rudp_pkge_t *>   m_waiting_echo;  // 等候响应hash表
	std::atomic<uint16_t>   m_status;
	
	uv_loop_t             * p_loop;
	uv_udp_t                m_udp;

	std::function< void ( err_code ) >     connect_cb;
	std::function< void ( err_code ) >     close_cb;
	std::function< void ( err_code ) >     send_cb;
	std::function< void ( const char * , size_t , err_code)>  recv_cb;
	std::function< void ( err_code ) >     ping_cb;
	std::function< void ( struct addrinfo * )> on_resolv_cb;
protected:
	/**
	 * @brief 初始化Libuv
	 */
	void init_uv();
	
	/**
	 * @brief 响应接收原始数据包的操作
	 * @param data , 原始的数据
	 * @param len , 数据长度
	 * @param peer , 对点地址
	 */
	void on_raw_recv( const char * data , size_t len , const struct sockaddr * peer);
	/**
	 * @brief 响应发送原始数据包操作完成
	 */
	void on_raw_send( rudp_pkge_t * package );
	/**
	 * @brief 
	 */
	void on_resolve( struct addrinfo * info );
	/**
	 * @brief 解析域名
	 */
        void resolver( const std::string& url , int port );
        void resolver( const std::string& url , int port , std::function< void ( const struct addrinfo*) >cb );
	/**
	 * @brief 打包用户数据数据
	 */
	static void packet( const char * data , size_t len , rudp_pkge_t ** pkge );
	/**
	 * @brief 构造反馈包
	 */
	static void packet_e( const rudp_pkge_t * pkgein , rudp_pkge_t ** pkgeout );
	
	static void packet_ping( rudp_pkge_t ** pkge );
	static void packet_ping_e( const rudp_pkge_t * pkgein , rudp_pkge_t ** pkgeout );
	
	static void packet_connect( rudp_pkge_t **pkge );
	static void packet_connect_e( const rudp_pkge_t * pkgein , rudp_pkge_t ** pkgeout );
	
	static void packet_close( rudp_pkge_t ** pkge );
	static void packet_close_e( const rudp_pkge_t * pkgein , rudp_pkge_t ** pkgeout );
	
	static void packet_keepalive( rudp_pkge_t ** pkge );
	static void packet_keepalive_e( const rudp_pkge_t * pkgein , rudp_pkge_t ** pkgeout );
	/**
	 * 解包数据
	 */
	static void unpacket( const char * data , size_t len , rudp_pkge_t ** pkge );

	/**
	 * @brief RUDP 的连接操作，构造连接数据包发送数据包
	 */
	void real_connect();
	/**
	 * @brief 响应发送数据完成操作，处理等候响应列表和等候响应
	*/
	void do_on_finish_send( rudp_pkge_t * pkge );
	
	/**
	 * @brief 响应接收数据。处理各个不同的操作。同时处理状态标志
	 * @param pkge 接收到的数据包
	*/
	virtual void do_on_send( rudp_pkge_t * pkge , const struct sockaddr * add ) = 0;
	void do_on_send_echo( rudp_pkge_t * pkge );
	void do_on_keepalive( rudp_pkge_t * pkge );
	void do_on_keepalive_echo( rudp_pkge_t * pkge );
	void do_on_ping( rudp_pkge_t * pkge );
	void do_on_ping_echo( rudp_pkge_t * pkge );
	virtual void do_on_connect( rudp_pkge_t * pkge  , const struct sockaddr * add) = 0;
	virtual void do_on_connect_echo( rudp_pkge_t * pkge , const struct sockaddr * add ) = 0;
	virtual void do_on_connect_echo_echo( rudp_pkge_t * pkge , const struct sockaddr * add) = 0;
	virtual void do_on_close( rudp_pkge_t * pkge , const struct sockaddr * add ) = 0;
	virtual void do_on_close_echo( rudp_pkge_t * pkge , const struct sockaddr * add ) = 0;
	
	/**
	 * @brief 发送UDP数据包。这个发送函数不考虑RUDP数据包结构，是RUDP底层的数据
	 *        通讯接口
	 * @param data 要发送的数据
	 * @param len 要发送的数据的长度
	 * @exceptions ERR_ALLOC_MEM , 内存分配失败。ERR_UV_UDP_SEND
	*/
	void raw_send( const char * data , size_t len );
	
	/**
	 * @brief 发送数据包
	 * @param p ， 要发送的数据包
	*/
	void send_packet( rudp_pkge_t * p );
	/**
	 * @brief 接收连接。这个函数对于客户端来说不做任何事情。在服务器端对象中
	 *        要对这个函数进行重载实现接受连接处理操作
	 * @param 对点地址
	*/
	virtual void on_accept( const struct sockaddr * add ){}
	virtual void on_close( const struct sockaddr * add ){}
	virtual void on_close_echo( const struct sockaddr * add ){}
public:
	/**
	 * 构造对象
	 */
	rudp();
	/**
	 * @brief 构造服务器对象
	*/
	rudp( int port );
	rudp( const std::string& url , int port );
        virtual ~rudp();
	/**
	 * @brief 连接服务器
	 * @param url
	 * @param port
	 * @return 
	 */
	err_code connect( const std::string& url , int port );
        void connect( const std::string& url , int port , std::function< void (err_code) > cb );
	/**
	 */
	err_code close();
	err_code evt_close( std::function<void(err_code)> cb );
	/**
	 * @brief 设置连接事件响应处理函数
	 */
	err_code evt_connected( std::function< void ( err_code )> cb );
	/**
	 * @brief 发送数据
	 * @param data ， 要发送的数据
	 * @param len ，要发送的数据长度
	 * @param dest , 目标地址
	 */
	err_code send( const char * data , size_t len );
	err_code send( const char * data , size_t len , const struct sockaddr * dest );
	/**
	 * @brief 
	 */
	void onsend( std::function<void ( err_code ) > cb );
	/**
	 * @brief 设置接收回调函数
	 */
	err_code evt_recv( std::function<void ( const char * , size_t , err_code )> cb );
	/**
	 * @brief 包活机制。
	 * @param sw
	 * @param intval , 包活包间隔时间单位ms
	 */
	void keepalive( bool sw , long intval );
	/**
	 * @brief 发送可达探测包
	 */
	err_code ping( int intval , int count = -1 );
	void ping_stop(){ ping_timer.Stop(); }
	err_code evt_ping( std::function< void ( err_code )> cb );
};
#endif
